#include "ConnectionPoll.h"
#include "public.h" // for log

#include <stdio.h> // for fopen()

// 获取连接池对象实例,  懒汉式单例函数接口
ConnectionPool *ConnectionPool::getConnectionPool()
{
    // 自C++11起，局部静态变量的初始化是线程安全的
    static ConnectionPool connPoll; // lock 和 unlock
    return &connPoll;
}

/* 外部使用如下：
    std::shared_ptr<Connection> connptr = cp->getConnection();
    sp->update(sql);
    } 出了右括号， 智能指针就会进行析构，又因为自定义了删除器，把Connection * p 放回了_connectionQue.push(p)中*/

// 给外部提供一个API， 从连接池中获取一个可用的空闲连接
std::shared_ptr<Connection> ConnectionPool::getIdleConn()
{
    std::unique_lock<std::mutex> lock(_queueMutex);
    if (_connectionQue.empty())
    { // 如果连接池队列为空，使用wait_for带有超时的等待_maxLatencyTime毫秒
        bool condition_met = _cv.wait_for(lock, std::chrono::milliseconds(_maxLatencyTime), [this]()
                                          { return !_connectionQue.empty(); });
        // 检查是否超时:在等待期间内没有可用的连接即_connectionQue.empty()一直为true）
        if (!condition_met)
        {
            LOG("获取空间连接超时了……获取连接失败!");
            return nullptr;
        }
    }
    // 条件满足， 即_connectionQue.empty() == false， 连接队列有库存
    /* shared_ptr智能指针析构时会把connection资源直接delete掉， 需要自定义删除器 */
    // 从队列中取出一个连接，并创建一个shared_ptr
    Connection *rawPtr = _connectionQue.front();
    std::shared_ptr<Connection> connPtr(rawPtr, [this](Connection *p)
                                        {
                                            std::unique_lock<std::mutex> lock(_queueMutex); // 1.加锁保护共享资源 _connectionQue 的访问
                                            p->refreshAliveTime();                          // 2.刷新一下开始空闲的起始时间
                                            _connectionQue.push(p);                         // 3.将连接指针 p 添加到队列 _connectionQue 中，以便后续重用
                                            // _cv.notify_all();                               // 3.使用条件变量 _cv 通知可能正在等待可用连接的线程
                                        });
    _connectionQue.pop();

    // 记录成功获取连接的时间戳
    auto now = std::chrono::system_clock::now();
    auto now_time_t = std::chrono::system_clock::to_time_t(now);
    LOG("成功获取连接：" + std::string(std::ctime(&now_time_t)));

    _cv.notify_all();

    return connPtr;
}

// 数据库连接池配置初始化
bool ConnectionPool::init()
{
    return loadConfigFile();
}

// 从mysql.cnf配置文件中加载配置项
bool ConnectionPool::loadConfigFile()
{
    FILE *pf = fopen("./src/mysql.cnf", "r");
    if (pf == nullptr)
    {
        LOG("mysql.cnf file is not exists!");
        return false;
    }

    while (!feof(pf))
    {
        // 取一行
        char line[256] = {0};
        fgets(line, sizeof(line), pf);
        // 通过string查找
        std::string str = line;
        int idx = str.find('=', 0);
        if (-1 == idx)
        {
            continue;
        }

        // password=123789\n
        int endidx = str.find('\n', idx);
        std::string key = str.substr(0, idx);
        std::string value = str.substr(idx + 1, endidx -idx -1);

        std::cout << "Key: " << key << ", Value: " << value << std::endl;
        if (key == "ip")
        {
            _ip = value;
        }
        else if (key == "port")
        {
            _port = std::stoi(value.c_str());
        }
        else if (key == "username")
        {
            _username = value;
        }
        else if (key == "password")
        {
            _password = value;
        }
        else if (key == "dbname")
        {
            _dbname = value;
        }
        else if (key == "initSize")
        {
            _initSize = std::stoi(value.c_str());
        }
        else if (key == "maxSize")
        {
            _maxSize = std::stoi(value.c_str());
        }
        else if (key == "maxIdleTime")
        {
            _maxIdleTime = std::stoi(value.c_str());
        }
        else if (key == "maxLatencyTime")
        {
            _maxLatencyTime = std::stoi(value.c_str());
        }
    }

    fclose(pf);
    return true;
}

// 线程函数: 运行在独立的线程中，专门负责生产新的连接
void ConnectionPool::produceConnectionTask()
{
    for (;;)
    {
        std::unique_lock<std::mutex> lock(_queueMutex);
        // 使用谓词来等待直到队列为空且当前连接数小于最大限制
        _cv.wait(lock, [this]()
                 { return _connectionQue.empty() && _connCnt < _maxSize; });

        // 队列为空 解除阻塞， 开始工作
        if (_connCnt < _maxSize)
        {
            // 创建一个新的连接并配置它
            auto p = std::make_unique<Connection>();
            p->connect(_ip, _port, _username, _password, _dbname);
            p->refreshAliveTime(); // 刷新一下开始空闲的起始时间
            // 将连接放入队列中，并增加计数
            _connectionQue.push(p.get());
            _connCnt++;
            // 释放所有权给队列中的元素，防止unique_ptr自动删除对象
            p.release();
        }
        // 通知消费者线程， 可以消费连接了
        _cv.notify_all();
    }
}

// 扫描超过_maxIdleTime秒的空闲连接， 进行多余的连接回收
void ConnectionPool::scanningConnectionTask()
{
    for (;;)
    {
        // 让当前线程暂停执行一段指定的时间 来模拟定时效果
        std::this_thread::sleep_for(std::chrono::seconds(_maxIdleTime));

        // 扫描整个队列， 释放多余的连接
        std::unique_lock<std::mutex> lock(_queueMutex);
        while (_connCnt > _initSize)
        {
            Connection *p = _connectionQue.front();
            // 如果存活空闲时间 > 最大空闲时候
            if (p->getAliveTime() > std::chrono::duration<double>(_maxIdleTime))
            {
                _connectionQue.pop();
                _connCnt--; // 调整连接队列的连接计数器
                delete p;   // 调用~Connection()释放连接
            }
            else
            { // 如果某一次的队头都没有超过_maxIdleTime，就直接跳出while()
                break;
            }
        }
    }
}

// 单例模式：构造函数私有化
ConnectionPool::ConnectionPool()
{
    // 1.加载配置项
    if (!init())
    {
        return;
    }

    // 2.根据配置进行数据库连接池的初始化
    for (int i = 0; i <= _initSize; i++)
    {
        Connection *p = new Connection();
        p->connect(_ip, _port, _username, _password, _username);
        p->refreshAliveTime(); // 刷新一下开始空闲的起始时间
        _connectionQue.push(p);
        _connCnt++;
    }

    // 启动一个生产线程， 作为[Producer]生产者线程, 把当前对象this，与类::成员函数地址绑定
    // 启动当前对象this::成员方法produceConnectionTask()
    std::thread produce(std::bind(&ConnectionPool::produceConnectionTask, this));
    produce.detach();

    // 启动一个新的定时线程, 负责扫描多余的空闲连接, 超过_maxIdleTime秒的空闲连接，则进行连接回收
    std::thread scanning(std::bind(&ConnectionPool::scanningConnectionTask, this));
    scanning.detach();
}
